#include "Emulador.h"
#include "Procesador.h"

void RunInstruction(BYTE Opcode)
{
    printf("\n%4X: %2X", PC, Opcode);

    switch(Opcode)
    {
        // 8-bit loads -> LD nn,n
        case 0x3E: CPU_8BIT_LOAD(&Reg_AF.HI); break;
        case 0x06: CPU_8BIT_LOAD(&Reg_BC.HI); break;
        case 0x0E: CPU_8BIT_LOAD(&Reg_BC.LO); break;
        case 0x16: CPU_8BIT_LOAD(&Reg_DE.HI); break;
        case 0x1E: CPU_8BIT_LOAD(&Reg_DE.LO); break;
        case 0x26: CPU_8BIT_LOAD(&Reg_HL.HI); break;
        case 0x2E: CPU_8BIT_LOAD(&Reg_HL.LO); break;

        // Registry loads -> LD r1,r2
        case 0x7F: CPU_REG_LOAD(&Reg_AF.HI, Reg_AF.HI); break;
        case 0x78: CPU_REG_LOAD(&Reg_AF.HI, Reg_BC.HI); break;
        case 0x79: CPU_REG_LOAD(&Reg_AF.HI, Reg_BC.LO); break;
        case 0x7A: CPU_REG_LOAD(&Reg_AF.HI, Reg_DE.HI); break;
        case 0x7B: CPU_REG_LOAD(&Reg_AF.HI, Reg_DE.LO); break;
        case 0x7C: CPU_REG_LOAD(&Reg_AF.HI, Reg_HL.HI); break;
        case 0x7D: CPU_REG_LOAD(&Reg_AF.HI, Reg_HL.LO); break;
        case 0x40: CPU_REG_LOAD(&Reg_BC.HI, Reg_BC.HI); break;
        case 0x41: CPU_REG_LOAD(&Reg_BC.HI, Reg_BC.LO); break;
        case 0x42: CPU_REG_LOAD(&Reg_BC.HI, Reg_DE.HI); break;
        case 0x43: CPU_REG_LOAD(&Reg_BC.HI, Reg_DE.LO); break;
        case 0x44: CPU_REG_LOAD(&Reg_BC.HI, Reg_HL.HI); break;
        case 0x45: CPU_REG_LOAD(&Reg_BC.HI, Reg_HL.LO); break;
        case 0x48: CPU_REG_LOAD(&Reg_BC.LO, Reg_BC.HI); break;
        case 0x49: CPU_REG_LOAD(&Reg_BC.LO, Reg_BC.LO); break;
        case 0x4A: CPU_REG_LOAD(&Reg_BC.LO, Reg_DE.HI); break;
        case 0x4B: CPU_REG_LOAD(&Reg_BC.LO, Reg_DE.LO); break;
        case 0x4C: CPU_REG_LOAD(&Reg_BC.LO, Reg_HL.HI); break;
        case 0x4D: CPU_REG_LOAD(&Reg_BC.LO, Reg_HL.LO); break;
        case 0x50: CPU_REG_LOAD(&Reg_DE.HI, Reg_BC.HI); break;
        case 0x51: CPU_REG_LOAD(&Reg_DE.HI, Reg_BC.LO); break;
        case 0x52: CPU_REG_LOAD(&Reg_DE.HI, Reg_DE.HI); break;
        case 0x53: CPU_REG_LOAD(&Reg_DE.HI, Reg_DE.LO); break;
        case 0x54: CPU_REG_LOAD(&Reg_DE.HI, Reg_HL.HI); break;
        case 0x55: CPU_REG_LOAD(&Reg_DE.HI, Reg_HL.LO); break;
        case 0x58: CPU_REG_LOAD(&Reg_DE.LO, Reg_BC.HI); break;
        case 0x59: CPU_REG_LOAD(&Reg_DE.LO, Reg_BC.LO); break;
        case 0x5A: CPU_REG_LOAD(&Reg_DE.LO, Reg_DE.HI); break;
        case 0x5B: CPU_REG_LOAD(&Reg_DE.LO, Reg_DE.LO); break;
        case 0x5C: CPU_REG_LOAD(&Reg_DE.LO, Reg_HL.HI); break;
        case 0x5D: CPU_REG_LOAD(&Reg_DE.LO, Reg_HL.LO); break;
        case 0x60: CPU_REG_LOAD(&Reg_HL.HI, Reg_BC.HI); break;
        case 0x61: CPU_REG_LOAD(&Reg_HL.HI, Reg_BC.LO); break;
        case 0x62: CPU_REG_LOAD(&Reg_HL.HI, Reg_DE.HI); break;
        case 0x63: CPU_REG_LOAD(&Reg_HL.HI, Reg_DE.LO); break;
        case 0x64: CPU_REG_LOAD(&Reg_HL.HI, Reg_HL.HI); break;
        case 0x65: CPU_REG_LOAD(&Reg_HL.HI, Reg_HL.LO); break;
        case 0x68: CPU_REG_LOAD(&Reg_HL.LO, Reg_BC.HI); break;
        case 0x69: CPU_REG_LOAD(&Reg_HL.LO, Reg_BC.LO); break;
        case 0x6A: CPU_REG_LOAD(&Reg_HL.LO, Reg_DE.HI); break;
        case 0x6B: CPU_REG_LOAD(&Reg_HL.LO, Reg_DE.LO); break;
        case 0x6C: CPU_REG_LOAD(&Reg_HL.LO, Reg_HL.HI); break;
        case 0x6D: CPU_REG_LOAD(&Reg_HL.LO, Reg_HL.LO); break;

        // 16-bit loads -> LD nn,n
        case 0x01: CPU_16BIT_LOAD(&Reg_BC.Reg); break;
        case 0x11: CPU_16BIT_LOAD(&Reg_DE.Reg); break;
        case 0x21: CPU_16BIT_LOAD(&Reg_HL.Reg); break;
        case 0x31: CPU_16BIT_LOAD(&Reg_SP.Reg); break;

        // 8-bit ADD / ADC -> ADD A,n / ADC A,n
        case 0x87: CPU_8BIT_ADDITION(Reg_AF.HI, 0, 0); break;
        case 0x80: CPU_8BIT_ADDITION(Reg_BC.HI, 0, 0); break;
        case 0x81: CPU_8BIT_ADDITION(Reg_BC.LO, 0, 0); break;
        case 0x82: CPU_8BIT_ADDITION(Reg_DE.HI, 0, 0); break;
        case 0x83: CPU_8BIT_ADDITION(Reg_DE.LO, 0, 0); break;
        case 0x84: CPU_8BIT_ADDITION(Reg_HL.HI, 0, 0); break;
        case 0x85: CPU_8BIT_ADDITION(Reg_HL.LO, 0, 0); break;
        case 0xC6: CPU_8BIT_ADDITION(0, 0, 1); break;
        case 0x8F: CPU_8BIT_ADDITION(Reg_AF.HI, 1, 0); break;
        case 0x88: CPU_8BIT_ADDITION(Reg_BC.HI, 1, 0); break;
        case 0x89: CPU_8BIT_ADDITION(Reg_BC.LO, 1, 0); break;
        case 0x8A: CPU_8BIT_ADDITION(Reg_DE.HI, 1, 0); break;
        case 0x8B: CPU_8BIT_ADDITION(Reg_DE.LO, 1, 0); break;
        case 0x8C: CPU_8BIT_ADDITION(Reg_HL.HI, 1, 0); break;
        case 0x8D: CPU_8BIT_ADDITION(Reg_HL.LO, 1, 0); break;
        case 0xCE: CPU_8BIT_ADDITION(0, 1, 1); break;

        // 8-bit SUB / SBC -> SUB A,n / SBC A,n
        case 0x97: CPU_8BIT_SUBTRACTION(Reg_AF.HI, 0, 0); break;
        case 0x90: CPU_8BIT_SUBTRACTION(Reg_BC.HI, 0, 0); break;
        case 0x91: CPU_8BIT_SUBTRACTION(Reg_BC.LO, 0, 0); break;
        case 0x92: CPU_8BIT_SUBTRACTION(Reg_DE.HI, 0, 0); break;
        case 0x93: CPU_8BIT_SUBTRACTION(Reg_DE.LO, 0, 0); break;
        case 0x94: CPU_8BIT_SUBTRACTION(Reg_HL.HI, 0, 0); break;
        case 0x95: CPU_8BIT_SUBTRACTION(Reg_HL.LO, 0, 0); break;
        case 0xD6: CPU_8BIT_SUBTRACTION(0, 0, 1); break;
        case 0x9F: CPU_8BIT_SUBTRACTION(Reg_AF.HI, 1, 0); break;
        case 0x98: CPU_8BIT_SUBTRACTION(Reg_BC.HI, 1, 0); break;
        case 0x99: CPU_8BIT_SUBTRACTION(Reg_BC.LO, 1, 0); break;
        case 0x9A: CPU_8BIT_SUBTRACTION(Reg_DE.HI, 1, 0); break;
        case 0x9B: CPU_8BIT_SUBTRACTION(Reg_DE.LO, 1, 0); break;
        case 0x9C: CPU_8BIT_SUBTRACTION(Reg_HL.HI, 1, 0); break;
        case 0x9D: CPU_8BIT_SUBTRACTION(Reg_HL.LO, 1, 0); break;
        case 0xDE: CPU_8BIT_SUBTRACTION(0, 1, 1); break;

        // 8-bit AND / OR / XOR -> AND n / OR n / XOR n
        case 0xA7: CPU_8BIT_AND_OR_XOR(Reg_AF.HI, OPERATION_AND, 0); break;
        case 0xA0: CPU_8BIT_AND_OR_XOR(Reg_BC.HI, OPERATION_AND, 0); break;
        case 0xA1: CPU_8BIT_AND_OR_XOR(Reg_BC.LO, OPERATION_AND, 0); break;
        case 0xA2: CPU_8BIT_AND_OR_XOR(Reg_DE.HI, OPERATION_AND, 0); break;
        case 0xA3: CPU_8BIT_AND_OR_XOR(Reg_DE.LO, OPERATION_AND, 0); break;
        case 0xA4: CPU_8BIT_AND_OR_XOR(Reg_HL.HI, OPERATION_AND, 0); break;
        case 0xA5: CPU_8BIT_AND_OR_XOR(Reg_HL.LO, OPERATION_AND, 0); break;
        case 0xE6: CPU_8BIT_AND_OR_XOR(0, OPERATION_AND, 1); break;
        case 0xB7: CPU_8BIT_AND_OR_XOR(Reg_AF.HI, OPERATION_OR, 0); break;
        case 0xB0: CPU_8BIT_AND_OR_XOR(Reg_BC.HI, OPERATION_OR, 0); break;
        case 0xB1: CPU_8BIT_AND_OR_XOR(Reg_BC.LO, OPERATION_OR, 0); break;
        case 0xB2: CPU_8BIT_AND_OR_XOR(Reg_DE.HI, OPERATION_OR, 0); break;
        case 0xB3: CPU_8BIT_AND_OR_XOR(Reg_DE.LO, OPERATION_OR, 0); break;
        case 0xB4: CPU_8BIT_AND_OR_XOR(Reg_HL.HI, OPERATION_OR, 0); break;
        case 0xB5: CPU_8BIT_AND_OR_XOR(Reg_HL.LO, OPERATION_OR, 0); break;
        case 0xF6: CPU_8BIT_AND_OR_XOR(0, OPERATION_OR, 1); break;
        case 0xAF: CPU_8BIT_AND_OR_XOR(Reg_AF.HI, OPERATION_XOR, 0); break;
        case 0xA8: CPU_8BIT_AND_OR_XOR(Reg_BC.HI, OPERATION_XOR, 0); break;
        case 0xA9: CPU_8BIT_AND_OR_XOR(Reg_BC.LO, OPERATION_XOR, 0); break;
        case 0xAA: CPU_8BIT_AND_OR_XOR(Reg_DE.HI, OPERATION_XOR, 0); break;
        case 0xAB: CPU_8BIT_AND_OR_XOR(Reg_DE.LO, OPERATION_XOR, 0); break;
        case 0xAC: CPU_8BIT_AND_OR_XOR(Reg_HL.HI, OPERATION_XOR, 0); break;
        case 0xAD: CPU_8BIT_AND_OR_XOR(Reg_HL.LO, OPERATION_XOR, 0); break;
        case 0xEE: CPU_8BIT_AND_OR_XOR(0, OPERATION_XOR, 1); break;

        // 8-bit CP -> CP n
        case 0xBF: CPU_8BIT_CP(Reg_AF.HI, 0); break;
        case 0xB8: CPU_8BIT_CP(Reg_BC.HI, 0); break;
        case 0xB9: CPU_8BIT_CP(Reg_BC.LO, 0); break;
        case 0xBA: CPU_8BIT_CP(Reg_DE.HI, 0); break;
        case 0xBB: CPU_8BIT_CP(Reg_DE.LO, 0); break;
        case 0xBC: CPU_8BIT_CP(Reg_HL.HI, 0); break;
        case 0xBD: CPU_8BIT_CP(Reg_HL.LO, 0); break;
        case 0xFE: CPU_8BIT_CP(0, 1); break;

        // 8-bit INC -> INC n
        case 0x3C: CPU_8BIT_INC(&Reg_AF.HI); break;
        case 0x04: CPU_8BIT_INC(&Reg_BC.HI); break;
        case 0x0C: CPU_8BIT_INC(&Reg_BC.LO); break;
        case 0x14: CPU_8BIT_INC(&Reg_DE.HI); break;
        case 0x1C: CPU_8BIT_INC(&Reg_DE.LO); break;
        case 0x24: CPU_8BIT_INC(&Reg_HL.HI); break;
        case 0x2C: CPU_8BIT_INC(&Reg_HL.LO); break;

        // Conditional jumps -> JR cc,n
        case 0x20: CPU_CONDITIONAL_JUMP(Z_FLAG, 0); break;
        case 0x28: CPU_CONDITIONAL_JUMP(Z_FLAG, 1); break;
        case 0x30: CPU_CONDITIONAL_JUMP(C_FLAG, 0); break;
        case 0x38: CPU_CONDITIONAL_JUMP(C_FLAG, 1); break;

        // Otro opcodes...
        case 0xCD: CPU_CALL_nn(); break;
        case 0xF3: CPU_DI(); break;
        case 0xC3: CPU_JP_nn(); break;
        case 0xF0: CPU_LDH_A_n(); break;
        case 0x00: CPU_NOP(); break;

        // Extended opcodes
        case 0xCB: RunExtendedInstruction(ROM[PC + 1]); break;

        default:
            printf("\n%4X: %2X ???", PC, Opcode);
            break;
    }

    return;
}

////////////////////////////////////////////////////////////////////

void ResetCPU(void)
{
    Reg_AF.Reg = 0x01B0;
    Reg_BC.Reg = 0x0013;
    Reg_DE.Reg = 0x00D8;
    Reg_HL.Reg = 0x014D;
    Reg_SP.Reg = 0xFFFE;

    ROM[0xFF05] = 0x00;
    ROM[0xFF06] = 0x00;
    ROM[0xFF07] = 0x00;
    ROM[0xFF10] = 0x80;
    ROM[0xFF11] = 0xBF;
    ROM[0xFF12] = 0xF3;
    ROM[0xFF14] = 0xBF;
    ROM[0xFF16] = 0x3F;
    ROM[0xFF17] = 0x00;
    ROM[0xFF19] = 0xBF;
    ROM[0xFF1A] = 0x7F;
    ROM[0xFF1B] = 0xFF;
    ROM[0xFF1C] = 0x9F;
    ROM[0xFF1E] = 0xBF;
    ROM[0xFF20] = 0xFF;
    ROM[0xFF21] = 0x00;
    ROM[0xFF22] = 0x00;
    ROM[0xFF23] = 0xBF;
    ROM[0xFF24] = 0x77;
    ROM[0xFF25] = 0xF3;
    ROM[0xFF26] = 0xF1;
    ROM[0xFF40] = 0x91;
    ROM[0xFF42] = 0x00;
    ROM[0xFF43] = 0x00;
    ROM[0xFF45] = 0x00;
    ROM[0xFF47] = 0xFC;
    ROM[0xFF48] = 0xFF;
    ROM[0xFF49] = 0xFF;
    ROM[0xFF4A] = 0x00;
    ROM[0xFF4B] = 0x00;
    ROM[0xFFFF] = 0x00;

    PC = 0x100;
    PendingInterruptsDisabled = 0;

    return;
}

////////////////////////////////////////////////////////////////////
// 8-bit loads -> LD nn,n

void CPU_8BIT_LOAD(BYTE *reg)
{
    (*reg) = ReadMemory(++PC);
    PC++;
    Cycles += 8;

    return;
}

////////////////////////////////////////////////////////////////////
// Registry loads -> LD r1,r2

void CPU_REG_LOAD(BYTE *reg, BYTE load)
{
    load = (*reg);
    PC++;
    Cycles += 4;

    return;
}

////////////////////////////////////////////////////////////////////
// 16-bit loads -> LD nn,n

void CPU_16BIT_LOAD(WORD *reg)
{
    (*reg) = (ReadMemory(++PC) | (ReadMemory(++PC) << 8));
    PC++;
    Cycles += 12;

    return;
}

////////////////////////////////////////////////////////////////////
// 8-bit ADD / ADC -> ADD A,n / ADC A,n

void CPU_8BIT_ADDITION(BYTE reg, int OpcodeIsADC, int UseImmValue)
{
    BYTE add_carry = (OpcodeIsADC == 1 ? C_FLAG : 0);
    BYTE imm_value = (UseImmValue == 1 ? ReadMemory(++PC) : 0);
    WORD src = Reg_AF.HI + reg + add_carry + imm_value;
    Z_FLAG = (src == 0 ? 1 : 0);
    N_FLAG = 0;
    Reg_HL.HI = ((Reg_AF.HI & 0x10) != (src & 0x10) ? 1 : 0);
    C_FLAG = (src > 0xFF ? 1 : 0);
    Reg_AF.HI = src & 0xFF;
    PC++;
    Cycles += (UseImmValue == 1 ? 8 : 4);

    return;
}

////////////////////////////////////////////////////////////////////
// 8-bit SUB / SBC -> SUB A,n / SBC A,n

void CPU_8BIT_SUBTRACTION(BYTE reg, int OpcodeIsSBC, int UseImmValue)
{
    BYTE sbc_carry = (OpcodeIsSBC == 1 ? 1 : 0);
    BYTE imm_value = (UseImmValue == 1 ? ReadMemory(++PC) : 0);
    WORD src = Reg_AF.HI - reg + sbc_carry - imm_value;
    Z_FLAG = (src == 0 ? 1 : 0);
    N_FLAG = 1;
    Reg_HL.HI = ((Reg_AF.HI & 0x10) != (src & 0x10) ? 1 : 0);
    C_FLAG = (((signed short)src) < 0 ? 1 : 0);
    PC++;
    Cycles += (UseImmValue == 1 ? 8 : 4);

    return;
}

////////////////////////////////////////////////////////////////////
// 8-bit AND / OR / XOR -> AND n / OR n / XOR n

void CPU_8BIT_AND_OR_XOR(BYTE reg, int Operation, int UseImmValue)
{
    switch(Operation)
    {
        case OPERATION_AND:
            Reg_AF.HI &= (UseImmValue == 1 ? ReadMemory(++PC) : reg);
            N_FLAG = C_FLAG = 0;
            Reg_HL.HI = 1;
            break;
        case OPERATION_OR:
            Reg_AF.HI |= (UseImmValue == 1 ? ReadMemory(++PC) : reg);
            N_FLAG = Reg_HL.HI = C_FLAG = 0;
            break;
        case OPERATION_XOR:
            Reg_AF.HI ^= (UseImmValue == 1 ? ReadMemory(++PC) : reg);
            N_FLAG = Reg_HL.HI = C_FLAG = 0;
            break;
    }
    Z_FLAG = (Reg_AF.HI == 0 ? 1 : 0);
    PC++;
    Cycles += (UseImmValue == 1 ? 8 : 4);

    return;
}

////////////////////////////////////////////////////////////////////
// 8-bit CP -> CP n

void CPU_8BIT_CP(BYTE reg, int UseImmValue)
{
    BYTE src = (UseImmValue == 1 ? ROM[++PC] : reg);
    BYTE res = Reg_AF.HI - src;
    Z_FLAG = (res == 0 ? 1 : 0);
    N_FLAG = 1;
    Reg_HL.HI = ((src & 0xF) > (Reg_AF.HI & 0xF) ? 1 : 0);
    C_FLAG = (Reg_AF.HI < src ? 1 : 0);
    PC++;
    Cycles += (UseImmValue == 1 ? 8 : 4);

    return;
}

////////////////////////////////////////////////////////////////////
// 8-bit INC -> INC n

void CPU_8BIT_INC(BYTE *reg)
{
    BYTE res = (*reg) + 1;
    Z_FLAG = (res == 0 ? 1 : 0);
    N_FLAG = 0;
    Reg_HL.HI = (((*reg) & 0xF) > (res & 0xF) ? 1 : 0);
    (*reg)++;
    PC++;
    Cycles += 4;

    return;
}

////////////////////////////////////////////////////////////////////
// Conditional jumps -> JR cc,n

void CPU_CONDITIONAL_JUMP(BYTE flag, BYTE value)
{
    if(flag == value) PC += (signed char)ROM[++PC];
    Cycles += 8;

    return;
}

////////////////////////////////////////////////////////////////////
// Otros opcodes...

void CPU_CALL_nn(void)
{
    ROM[Reg_SP.Reg--] = (PC + 3) >> 8;
    ROM[Reg_SP.Reg--] = (PC + 3) & 0xFF;
    PC = ROM[++PC] | (ROM[++PC] << 8);
    Cycles += 12;

    return;
}

void CPU_DI(void)
{
    PendingInterruptsDisabled = 2;
    PC++;
    Cycles += 4;

    return;
}

void CPU_JP_nn(void)
{
    PC = (ROM[++PC] | (ROM[++PC] << 8));
    Cycles += 12;

    return;
}

void CPU_LDH_A_n(void)
{
    Reg_AF.HI = ReadMemory(0xFF00 + ROM[++PC]);
    PC++;
    Cycles += 12;

    return;
}

void CPU_NOP(void)
{
    PC++;
    Cycles += 4;

    return;
}

////////////////////////////////////////////////////////////////////
// Extended opcodes

void RunExtendedInstruction(BYTE ExtendedOpcode)
{
    switch(ExtendedOpcode)
    {
        default:
            printf(" %2X ???", ExtendedOpcode);
            break;
    }

    return;
}

////////////////////////////////////////////////////////////////////
// Bit manupulation BIT -> BIT b,r

void CPU_EXTENDED_BIT_b_r(BYTE bit, BYTE reg)
